/**
 * @brief 共享内存帧管理
 *  注意，对于阻塞写方式（SHAREMEM_WRITE_BLOCK），目前只支持一对一的方式，并且推荐使用int GetOneFrame( char* buffer, int length, int& flag )来获取数据，避免覆盖
 * */

#ifndef __STREAMBUFFER_H__
#define __STREAMBUFFER_H__
#include "../general/Semaphore.h"

namespace Common{

typedef enum{
	SHAREMEM_READ_MODE = 0, /**< 注册方式 只读 */
	SHAREMEM_WRITE_MODE = 1, /**< 注册方式 只写 */
	SHAREMEM_WRITE_BLOCK = 2, /**< 注册方式 阻塞写 */
	SHAREMEM_READ_64KMODE = 3 /**< 注册方式以64K为单位读 */
}EnumSharememoryMode;

typedef enum{
	SHAREMEM_FLAG_PFRAME = 0,  /**< P帧 */
	SHAREMEM_FLAG_IFRAME = 1, /**< I帧 */
	SHAREMEM_FLAG_AFRAME = 2, /**< 音频帧 */
	SHAREMEM_FLAG_HFRAME = 3, /**< 用于备份的录像文件头 */
	SHAREMEM_FLAG_EFRAME = 4 /**< 用于备份的结束标记 */
}EnumSharememoryFrameType;

struct SShareMemFrameInfo{
	unsigned char year; /**< 年 from 2000 */
	unsigned char month; /**< 月 1~12 */
	unsigned char day; /**< 日 1~31 */
	unsigned char hour; /**< 0~23 */
	unsigned char minute; /**< 0~59 */
	unsigned char second; /**< 0~59 */
	unsigned char flag; /**< 参考 EnumSharememoryFrameType  */
	unsigned char cut:2; /**< 分段标记 1：需要分段,该标记作为新段的开始需要保证该帧为I帧 */
	unsigned char standard:2; /**< 制式 */
	unsigned char resolution:4; /**< 分辨率 */
};

/**
 * @共享内存数据结构
 * */
struct SShareMemData{
	unsigned int iPackCount;
	struct _framePack_{
		char* pAddr;
		unsigned int iLen;
	} pstuPack[16];
};

/**
 * @brief 以64K为单位记录BUFFER中的帧信息（目前只记录I帧）
 * */
struct SShareMem64kInfo{
	unsigned char ucFrameCount; /**< 该段缓冲中的帧数 */
	char reserved[3];
	struct _ShareMem64KFrameInfo_{
		unsigned int iOffSet; /**< 帧相对与该段缓冲的偏移 */
		unsigned long ulPtsHigh; /**< PTS */
		unsigned long ulPtsLow; /**< PTS */
		SShareMemFrameInfo* info; /**< 帧信息 */
	} stuInfo[20];
};


class CStreamBuffer
{
public:
	/**
	 * @brief 构造函数。
	 * @param id
	 * @param name  需要注册的共享内存,长度不超过32个字符。
	 * @param size  共享内存大小，同一块共享内存，大小必须相同。
	 * @param frames 共享内存中可存放的最大帧数, 同一个共享内存，帧数必须相同
	 * @param mode  读写方式
	 */
	CStreamBuffer(const char *id, const char *name, int size, int frames, EnumSharememoryMode mode);

	virtual ~CStreamBuffer();

public:
	/**
	 * @brief 向共享内存中放入一帧数据
	 * @param pData 数据结构，
	 * @param info 帧信息
	 * @return 写入的字节数
	 * */
	int PutOneFrame( const SShareMemData* pPack, const SShareMemFrameInfo* info );

	/**
	 * @brief 从共享内存中读取一帧数据
	 * @param pPack 帧数据
	 * @param info 帧信息
	 * @return 0表示无帧可读，否则为当前帧长度
	 * */
	int GetOneFrame( SShareMemData* pPack, int& flag );

	/**
	 * @brief 从共享内存中读取帧(不推荐使用在获取码流上)。
	 * @param buffer  帧数据缓冲区地址。
	 * @param info    帧信息。
	 * @return 当前帧数据长度，0表示无帧可读。
	 */
	int GetOneFrame( char ** buffer, SShareMemFrameInfo** info );

	/**
	 * @brief 以64K为单位，从共享内存中获取数据，用于录像（录像写入文件时以64K为单位）
	 * @param buffer 数据指针
	 * @param info 数据
	 * @return 0表示无数据或数据不足64K，否则返回64K
	 * */
	int Get64kData( char** buffer, SShareMem64kInfo* info, int iBlocks=1, bool bGetPts=true );

	/**
	 * @brief 跳转到下一个I帧
	 * @param	无
	 * @return	 成功返回true, 否则返回false.
	 */
	bool GotoNextIFrame(void);

	/**
	 * @brief 该接口针对网传，获取当前读指针到写指针见共有多少个I帧，以便网传的丢帧优化处理
	 * */
	int GetIFrameNum(void);

	/**
	 * @brief 跳到最新的一个I帧
	 * */
	bool GotoLastestIFrame(void);

	/**
	 * @brief 往前跳帧,用于预录
	 * @param second 跳转的时间
	 * @param info 输出参数，定位到的帧信息
	 * @return 失败-1 成功返回实际跳转了多少秒
	 * */
	int GotoPrevIFrame(int second, SShareMemFrameInfo* info);

	/**
	 * @brief 往前跳帧，用于起停录像时带来的丢秒问题，
	 *  该接口将会跳转到<= hour:minute:second，且离hour:minute:second最近的一个I帧
	 * @param hour, minute, second 时间
	 * @return 0 成功 否则失败
	 * */
	int GotoPrevIFrame( int hour, int minute, int second, SShareMemFrameInfo* info );

	/**
	 * @brief 跳转到录像文件头
	 * @return 0：文件头已找到，并跳转； -1：缓冲区中未找到; -2:播放文件已结束
	 * */
	int GotoHFrame(void);

	/**
	 * @brief 清除共享内存中的所有帧,该接口目前只供数据写入者调用，其他调用返回失败
	 * @return 0成功，否则失败
	 * */
	int ClearAllFrames();

	/**
	 *  @brief 获取共享内存中没有读的帧数
	 * 高16位标记当前帧是否有效，低16位表示读写序列的差值
	 *  */
	int GetLeftFrameCount();


public: //externed method
	/**
	 * @brief 向共享内存中写入一帧数据。
	 * @param buffer  数据。
	 * @param length  数据长度。
	 * @param flag   写入数据的标志。
	 * @return 写入数据的长度, 0 表示写入失败
	 */
	int PutOneFrame( const char* buffer, int length, int flag );

	/**
	 * @brief 向缓冲区读入一帧数据。
	 * @param buffer  数据读取缓冲区。
	 * @param length  缓冲区长度。
	 * @param flag    数据标志。
	 * @return 读入缓冲区的数据长度。
	 */
	int GetOneFrame( char* buffer, int length, int& flag );

	/**
	 * @brief 从共享内存中读取帧。
	 * @param buffer  帧数据缓冲区地址。
	 * @param flag    数据标志。
	 * @return 当前帧数据长度。
	 */
	int GetOneFrame(char **buffer, int &flag);

	/**
	 * @brief 从共享内存中读取一帧数据。该接口会分析帧帧结构获取时间戳，不需要时间戳则pts传入NULL，以提高效率
	 * @param pPack 帧数据（可能有两包组成一帧的情况）
	 * @param info 帧信息
	 * @return 0表示无帧可读，否则为当前帧长度
	 * */
	int GetOneFrame( SShareMemData* pPack, SShareMemFrameInfo** info );

	void PrintBufferInfo( void* param );

	int GetOneFrame( char* buffer, int length, SShareMemFrameInfo** info );

	int CheckNetworkJump( int percent );

private:

	void *CreateShareMemory(const char *name, int size, int frames);

	void DeleteShareMemory();

	void InitSharememoryParam();

	int CalcPackLen( const SShareMemData* pPack );

	int SetInvalidFrame( int iFrameMemLen );

	unsigned int GetValidFramesSize();

	int JumpToOldestIFrame();

	int JumpToOldestFrame();

	int GetWriterFrameValidFlag();

	int Reset64KShareMemReadPos( unsigned int uiPos );

	int Calc64kFrameInfo( unsigned int uiRdSharememPos, SShareMem64kInfo* info );

private:

	int m_iShareFd; /**< 共享内存文件句柄 */

	EnumSharememoryMode m_mode; /**< 当前用户到读写模式 */

	unsigned int m_memSize; /**< 共享内存实际帧缓冲大小 */
	unsigned int m_memValidSize; /**< 共享内存有效数据长度 */
	unsigned int m_iValidPos; /**< 有效的最后帧数 */

	unsigned short m_Frames; /**< 共享内存中的帧数 */

	char m_szShareName[32]; /**< 共享内存名称 */
	char m_szUserId[32];

	char *m_shareMemory; /**< 共享内存帧数据的起始指针  */

	struct SSharememoryHead{
		unsigned int uiWritFrameCount; /**< 总共写入的帧数，用户读用户到回环判断 */
		unsigned char ucReaderCount; /**< 读数据的人数 */
		unsigned char bLock; /**< 是否为阻塞写，0-否； 1-是 */
		char reserved[2];
	}* m_pstuHead; /**< 共享内存头 */

	struct SSharememoryFrame{
		unsigned int ucValidFlag; /**< 帧有效性标记  */
		//unsigned int iFrameMemlen:24; /**< 帧在共享内存中占用的长度（启用环回策略，该值不再有效） */
		unsigned int position; /**< the offset from frame buffer header */
		unsigned int len; /**< the frame length */
		SShareMemFrameInfo stuFrameInfo; /**< 帧信息 */
	}* m_pstuFrames; /**< 共享内存帧列 */

	char* m_pszCircleData; /**< 回环数据，再不回环的接口使用 */
	unsigned int m_uiCircleBufferLen;

	unsigned int m_iRdFrameCount;
	unsigned int m_iRdSharememPos; /**< 读指针在共享内存中的位置，用于64K块读取时使用 */
	bool m_bResetPos; /**< 标记读指针是否被重置过，用于脏数据的标记。Reset64KShareMemReadPos时标记 Get64kData时取消标记 */

	bool m_bMarkContinue;
	unsigned int m_u32MarkLeftLen;
};

};

#endif
